package com.strato.mvc.annotation.v1_0_0.handler;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.List;

import org.osgi.framework.Bundle;

import com.strato.annotation.base.v1_0_0.handler.AbstractAnnotationHandler;
import com.strato.annotation.base.v1_0_0.registry.BundleDescriptor;
import com.strato.annotation.base.v1_0_0.registry.ClassDescriptor;
import com.strato.annotation.base.v1_0_0.registry.MethodArgumentDescriptor;
import com.strato.annotation.base.v1_0_0.registry.MethodDescriptor;
import com.strato.base.bean.v1_0_0.BeanUtil;
import com.strato.base.env.v1_0_0.GlobalEnvironment;
import com.strato.base.osgi.v1_0_0.BundleUtil;
import com.strato.mvc.annotation.v1_0_0.annotation.type.Controller;
import com.strato.mvc.annotation.v1_0_0.annotation.type.RequestMapping;
import com.strato.mvc.annotation.v1_0_0.invoker.MethodHttpInvoker;
import com.strato.mvc.api.v1_0_0.control.PathVariable;
import com.strato.mvc.api.v1_0_0.control.RequestMappingRegistry;

public class ControllerAnnotationHandler extends AbstractAnnotationHandler {

	@Override
	public void onDiscoverClass(Bundle bundle, Class<?> clazz, ClassDescriptor cd) {
		List<Annotation> annotations = cd.getAnnotations();
		if(annotations!=null){
			for(Annotation annotation:annotations){
				discoverControllers(bundle,clazz,annotation,cd);
			}
		}
	}

	private void discoverControllers(Bundle bundle, Class<?> clazz, Annotation annotation, ClassDescriptor classDescriptor) {
		if (!Controller.class.equals(annotation.annotationType())) {
			return;
		}
		RequestMapping rm=clazz.getAnnotation(RequestMapping.class);
		String typeRequestMapping="";
		if(rm!=null){
			String v=rm.value();
			typeRequestMapping=GlobalEnvironment.getInstance().resolveString(v);
		}
		Class<com.strato.mvc.annotation.v1_0_0.annotation.method.RequestMapping> rmClass = com.strato.mvc.annotation.v1_0_0.annotation.method.RequestMapping.class;
		List<MethodDescriptor> methodDescriptors=classDescriptor.findMethodDescriptors(rmClass);
		RequestMappingRegistry rmRegistry=RequestMappingRegistry.getInstance();
		for(MethodDescriptor md:methodDescriptors){
			List<Class<?>> parameterTypeList=new ArrayList<Class<?>>();
			for(MethodArgumentDescriptor ad:md.getArgumentDescriptors()){
				try {
					parameterTypeList.add(BundleUtil.loadClass(bundle, ad.getTypeName()));
				} catch (ClassNotFoundException e) {
					throw new RuntimeException(e);
				}
			}
			Class<?>[] parameterTypes = parameterTypeList.toArray(new Class<?>[parameterTypeList.size()]);
			Method method = BeanUtil.getMethod(clazz, md.getMethodName(), parameterTypes);
			com.strato.mvc.annotation.v1_0_0.annotation.method.RequestMapping mrm=method.getAnnotation(rmClass);
			String methodRequestMapping=GlobalEnvironment.getInstance().resolveString(mrm.value());
			String fullRequestMapping=typeRequestMapping+methodRequestMapping;
			MethodHttpInvoker invoker=new MethodHttpInvoker();
			{
				invoker.setBundleName(bundle.getSymbolicName());
				invoker.setClassName(clazz.getName());
				invoker.setMethodName(method.getName());
				List<String> methodParameters=new ArrayList<String>();
				Parameter[] params = method.getParameters();
				for(Parameter param:params){
					methodParameters.add(param.getType().getName());
				}
				invoker.setMethodParameters(methodParameters);
			}
			List<PathVariable> pathVariables=new ArrayList<PathVariable>();
			for(Parameter arg:method.getParameters()){
				com.strato.mvc.annotation.v1_0_0.annotation.method.PathVariable pva=arg.getAnnotation(com.strato.mvc.annotation.v1_0_0.annotation.method.PathVariable.class);
				if(pva!=null){
					String argName=arg.getName();
					Class<?> argType=arg.getType();
					PathVariable pathVariable=new PathVariable(argName,argType);
					pathVariables.add(pathVariable);
				}
			}
			rmRegistry.addRequestMapping(fullRequestMapping, pathVariables, invoker);
		}
	}

	@Override
	public void handleUnresolve(Bundle bundle,BundleDescriptor bundleDescriptor) {
		RequestMappingRegistry rmRegistry=RequestMappingRegistry.getInstance();
		rmRegistry.removeInvokersByBundleName(bundle.getSymbolicName());
	}

}
